﻿using UnityEngine;
using System.Collections;
using CinemaDirector;
using System.Collections.Generic;
using TTGame;
/*
 * SkillTimelineSystem
 * 技能系统，使用事件组合机制。
 * （名字应该是SkillSystem，为了防止旧代码类重名）
 *
 * 使用动画脚本的Timeline编辑器作为技能编辑器。
 * 每个人物身上一个SkillSystem,负责整个施法过程的流程。
 *
 * mark:优化：添加施法队列概念。
 *      解决短时间按了多次施法，不得不中断或者等待的问题
 *      技能的同步也可以收益。
 *
 * 依赖：
 * Puppet, ObjectManager, SkillTL
 *
 * ！不写重复代码！
 * @author TTun
 * @date 7/13/2017 9:23:17 PM
 */
namespace TTGameEngine {

public delegate void OnSkillsCastBegin(SkillTL skillTL);
public delegate void OnSkillsFinished(SkillTL skillTL);

public class SkillTimelineSystem : MonoBehaviour {

//    static SkillTimelineSystem m_ins = null;

    static public bool SkillSystemLogFlag = true;

    SkillTL m_curSkill;
    [SerializeField]
    Puppet m_curCaster;
    UISelectorInfo m_autoSelectorAdapter;

    OnSkillsCastBegin cb_skillCastBegin;
    OnSkillsFinished cb_skillFinished;

    public SkillTL CurSkill {
        get { return m_curSkill; }
    }

    void Awake() {
        m_curCaster = GetComponent<Puppet>();
    }

    SkillCheckResult skillProgress = SkillCheckResult.SkillRet_OK;

    public void SetAutoSelectorAdapter(UISelectorInfo autoSel) {
        m_autoSelectorAdapter = autoSel;
    }

    Dictionary<uint, SkillHandler> skillHandlers = new Dictionary<uint, SkillHandler>();
    public void RegistSkillHandler(SkillHandler skillHandler) {
        skillHandlers.Add(skillHandler.m_castID, skillHandler);
    }

    /// <summary>
    /// 施法（播放技能脚本）
    ///
    /// 对象的状态机状态，会在脚本事件中去设置。比如播放技能动画的时候。
    /// </summary>
    /// <param name="skillID">技能ID</param>
    /// <param name="playerObj">放技能的对象。用于设置脚本的ActorGameObject。</param>
    public SkillCheckResult CastSkill(int skillID) {
        SkillTL newSkill = m_curCaster.SkillPackage.GetSkillByID(skillID);
        if(newSkill == null) {
            skillProgress = SkillCheckResult.SkillRet_Fail_NoSkill;
            SkillSysLogWarning("施法失败. Progress:[{0}]", skillProgress.ToString());
            return skillProgress;
        }
        return CastSkill(newSkill);
    }

    public SkillCheckResult CastSkill(SkillTL skill) {
        int skillID = skill == null ? -1 : skill.SkillID;
        SkillSysLog("开始准备施法. skillID:[{0}]", skillID);
        //0.检错判断
        if(m_curSkill == null && m_NewSkill != null) {
            m_NewSkill = null;
            SkillSysLogError("检测到错误。之前的技能没有正常施放。上一个等待释放的技能[{0}] 当前要释放的技能[{1}]", m_NewSkill.DumpSkill(), skill.DumpSkill());
        }

        //1.检测释放条件，是否正在施法/装弹
        skillProgress = checkSkill(skill, m_curCaster);
        if(skillProgress != SkillCheckResult.SkillRet_OK) {
            SkillSysLogWarning("施法失败. Progress:[{0}] skillID:[{1}]", skillProgress.ToString(), skillID);
            return skillProgress;
        }

        //2.装载新技能
        loadSkill(skill, m_curCaster);
        //3.进入释放流程
        if(isCasting == false)
            StartCoroutine(Process_CastSkill());

        SkillSysLog("施法调用返回. Progress:[{0}] ", skillProgress.ToString());
        return skillProgress;
    }
    SkillCheckResult checkSkill(SkillTL newSkill, Puppet caster) {
        SkillCheckResult ret = SkillCheckResult.SkillRet_OK;
        //为空判断
        if(newSkill == null) {
            ret = SkillCheckResult.SkillRet_Fail_NoSkill;
            return ret;
        }
        //技能是否满足释放条件。技能CD、子弹、怒气。
        ret = newSkill.CheckCast(caster);
        if(ret != SkillCheckResult.SkillRet_OK) {
            return ret;
        }

        //是否在施法中
        ret = IsRelasing(newSkill.SkillID);
        if(ret != SkillCheckResult.SkillRet_OK) {
            return ret;
        }
        return ret;
    }

    void OnDisable() {
        StopAllCoroutines();
        isCasting = false;
    }

    void loadSkill(SkillTL newSkill, Puppet caster) {
        m_NewSkill = newSkill;
        m_NewSkill.SetActive(true);
        m_NewSkill.m_caster = caster;
        m_NewSkill.Debug_SetCaster(m_curCaster);
        m_NewSkill.ResetCastID();
    }

    bool isCasting = false;
    ///newSkill是指将要播放的技能，curSkill是正在播放的技能。newSkill和curSkill都不为空，意味着正在等待curSkill打断，然后播放newSkill。
    SkillTL m_NewSkill = null;
    //释放技能流程
    IEnumerator Process_CastSkill() {
        if(m_NewSkill != null) {
            m_curSkill = m_NewSkill;
            m_NewSkill = null;
        }
        while(m_curSkill != null) {
            SkillSysLog("Casting Process Begin...");
            isCasting = true;
            //1.是否需要手动选择目标
            m_curSkill.m_selector.SetOwnerObj(m_curCaster);
            if(isNeedTargetSelector()) {
                skillProgress = SkillCheckResult.SkillRet_WaitSelectTarget;

                if(m_autoSelectorAdapter != null) {
                    StartSkillCircle(m_curCaster, m_curSkill, m_autoSelectorAdapter);
                }
                //进入选择目标模式，等待完成
                yield return StartCoroutine(Process_StartSelectTarget());
            } else {
                m_curSkill.m_selector.SetResult_SelfCenter(SkillSelectorType.SelfCenter);
            }

            if(m_curSkill == null) break;

            //2.再次检测施法条件
            skillProgress = CheckBeforeCast(m_curCaster);
            SkillSysLog("Casting Process - CheckCast[{0}]...", skillProgress.ToString());
            //3.施法
            if(skillProgress == SkillCheckResult.SkillRet_OK) {
                //正式播放Skill前，先等待一帧，好让外面切Skill状态
                yield return null;
                if(m_curSkill != null) {
                    skillProgress = m_curSkill.CastSkill(m_curCaster);
                    SkillSysLog("Casting Process - CastSkill[{0}]...", skillProgress.ToString());
                    if(skillProgress == SkillCheckResult.SkillRet_OK) {
                        skillProgress = SkillCheckResult.SkillRet_SkillCasting;
                        onSkillCastBegin();
                        RegistSkillHandler(m_curSkill.SkillLogicHandler);
                        while(m_NewSkill == null && m_curSkill != null && false == m_curSkill.IsSkillCutSceneEnd()) {
                            yield return null;
                        }
                        SkillSysLog("Casting Process - CastFinish isSkillCutSceneEnd[{0}]...", (m_curSkill != null ? m_curSkill.IsSkillCutSceneEnd().ToString() : "Null Skill"));
                        skillProgress = SkillCheckResult.SkillRet_CastFinish;
                    }
                } else {
                    SkillSysLogError("Null Skill");
                    skillProgress = SkillCheckResult.SkillRet_Exception_NullSkill;
                }
            }

            onFinishSkill();
        }
        isCasting = false;
    }

    public void onSkillCastBegin() {
        SkillSysLog(">>>>> 施法中...");
        if(cb_skillCastBegin != null) cb_skillCastBegin(m_curSkill);
        if(m_curSkill != null) {
            m_curSkill.triggerSkillBeigin();
        }
    }

    public void onFinishSkill() {
        SkillSysLog(">>>>> 施法结束");
        if(cb_skillFinished != null) cb_skillFinished(m_curSkill);
        //如果是主动Stop，m_curSkill和m_NewSkill都是Stop，此时不用管State了。
        if(m_curSkill != null && m_NewSkill == null && m_curCaster.StateID == PuppetStateID.Skill) {
            m_curCaster.Stand();
        }
        if(m_curSkill != null) {
            m_curSkill.triggerSkillFinished();
            m_curSkill.Revert();
            m_curSkill = null;
        }
        if(m_NewSkill != null) {
            m_curSkill = m_NewSkill;
            m_NewSkill = null;
        }
    }

    //注：不要在技能脚本开始时，调用停止技能。会导致技能开始事件无法触发。技能开始事件中目前包含怒气扣除逻辑。
    public void StopSkill() {
        SkillSysLog("Stop Skill. skillID:[{0}]", m_curSkill != null ? m_curSkill.SkillID.ToString() : "null");
        if(skillProgress == SkillCheckResult.SkillRet_SkillCasting) {
            if(cb_skillFinished != null) cb_skillFinished(m_curSkill);
            if(m_curSkill != null) {
                m_curSkill.triggerSkillFinished();
                m_curSkill.Revert();
            }
        }

        if(m_curSkill != null) m_curSkill.StopSkill();

        m_NewSkill = null;
        m_curSkill = null;
    }

    public void RegistCB_SkillsCastBegin(OnSkillsCastBegin cb) {
        cb_skillCastBegin -= cb;
        cb_skillCastBegin += cb;
    }

    public void RegistCB_SkillsFinished(OnSkillsFinished cb) {
        cb_skillFinished -= cb;
        cb_skillFinished += cb;
    }

    bool m_isPause;
    public bool IsPause() {
        return m_isPause;
    }
    public void Pause() {
        SkillSysLog("！暂停技能！");
        m_isPause = true;
        if(m_curSkill != null) {
            m_curSkill.Pause();
        }
    }

    public void Resume() {
        SkillSysLog("！恢复技能！");
        m_isPause = false;
        if(m_curSkill != null) {
            m_curSkill.Resume();
        }
    }

    public bool IsCasting() {
        return m_curSkill != null || m_NewSkill != null;
    }

    public enum SkillInterruptEvent {
        NewSkill,
        Hurt,
        Stun,
    }

    public SkillCheckResult TryInterrupt(SkillInterruptEvent intrEvent, int newSkillID) {
        if(m_curSkill == null) {
            SkillSysLog("打断当前技能! newSkill:[{0}]  curSkill:[null]", newSkillID);
            return SkillCheckResult.SkillRet_OK;
        }
        if(intrEvent == SkillInterruptEvent.NewSkill) {
            //重复释放技能，不打断
            if(newSkillID != -1 && m_curSkill.SkillID == newSkillID) {
                SkillSysLog("重复技能不打断，释放失败! newSkill:[{0}]  curSkill:[{1}]", newSkillID, m_curSkill.SkillID);
                return SkillCheckResult.SkillRet_INTR_Fail_Releasing;
            }
            if(m_curSkill.SkillType == SkillType.ActiveSkill) {
                return SkillCheckResult.SkillRet_INTR_Fail_Releasing;
            }
        }

        //正在释放其他技能，且不能打断
        bool ret = m_curSkill.TryInterrupt();
        if(ret == false) {
            SkillSysLog("当前释放技能不能被打断，释放失败! newSkill:[{0}]  curSkill:[{1}]", newSkillID, m_curSkill.SkillID);
            return SkillCheckResult.SkillRet_INTR_Fail_Releasing;
        }

        SkillSysLog("打断当前技能! newSkill:[{0}]  curSkill:[{1}]", newSkillID, m_curSkill.SkillID);
        return SkillCheckResult.SkillRet_OK;
    }

    public SkillCheckResult IsRelasing(int skillID) {
        if(m_curSkill != null) {
            //之前有技能正在播放
            return TryInterrupt(SkillInterruptEvent.NewSkill, skillID);
        }
        return SkillCheckResult.SkillRet_OK;
    }

    IEnumerator Process_StartSelectTarget() {
        //1、激活地上的圈。传递Selector。
        //2、激活UI。

        //3、等待Selector设置好&取消选目标
        while(m_NewSkill == null && m_curSkill.m_selector.IsSelected) {
            yield return null;
        }
    }


    //激活英雄的技能圈。关闭是由PuppetSkillCircle检测UISelectorInfo结果。
    public void StartSkillCircle(Puppet caster, int skillID, UISelectorInfo selectorInfo) {
        SkillTL skill = caster.SkillPackage.GetAISkillByIdx(skillID);
        StartSkillCircle(caster, skill, selectorInfo);
    }

    public void StartSkillCircle(Puppet caster, SkillTL skill, UISelectorInfo selectorInfo) {
        if(caster.GetSkillCircle().IsOnUse()) {
            DebugUtil.LogErrorFormat("[SkillTLSystem] SkillCircle is on use. skillID:[{0}]", skill.SkillID);
            return;
        }
        if(skill != null) {
            caster.GetSkillCircle().StartSelectMode(skill.m_selector, selectorInfo);
        } else {
            DebugUtil.LogErrorFormat("[SkillTLSystem] skill not found. skillID:[{0}]", skill.SkillID);
        }
    }

    bool isNeedTargetSelector() {
        if(m_curSkill.GetSelectorType() != SkillSelectorType.SelfCenter) {
            return true;
        } else {
            return false;
        }
    }

    //施法前最后检测一次施法条件。因为从装载技能到真正释放可能过了一会儿了，主要检测范围、目标等。
    SkillCheckResult CheckBeforeCast(Puppet caster) {
        SkillCheckResult result = SkillCheckResult.SkillRet_OK;
        //检测施法条件(范围、目标)

        result = m_curSkill.CheckCast(caster);

        return result;
    }

    public float GetSkillCastRange(SkillTL skill) {
        return skill.GetSkillCastRange();
    }

    //根据技能配置的信息，获取对象目标
    public List<Puppet> GetSkillTargetObjs(Puppet caster) {

        return null;
    }

    public string DumpSkillSysString(string msgformat, params object[] args) {
        return string.Format("[SkillSys] {0} \nobj[{1}] curSkillID[{2}] castID[{3}] NewSkillID[{4}] State[{5}]",
                             string.Format(msgformat, args),
                             m_curCaster != null ? m_curCaster.GetGID() : "No Caster",
                             m_curSkill != null ? m_curSkill.SkillID.ToString() : "null",
                             m_curSkill != null ? m_curSkill.SkillCastID.ToString() : "0",
                             m_NewSkill != null ? m_NewSkill.SkillID.ToString() : "null",
                             skillProgress
                            );
    }

    void SkillSysLog(string msgformat, params object[] args) {
        if(SkillSystemLogFlag == false) {
            return;
        }
        DebugUtil.LogFormat(DumpSkillSysString(msgformat, args));
    }
    void SkillSysLogWarning(string msgformat, params object[] args) {
        DebugUtil.LogFormat(DumpSkillSysString(msgformat, args));
    }

    void SkillSysLogError(string msgformat, params object[] args) {
        DebugUtil.LogFormat(DumpSkillSysString(msgformat, args));
    }

    public bool IsReleaseLoopSkill() {
        if(m_curSkill != null) {
            return m_curSkill.IsLoopSkill();
        }
        return false;
    }

    public void BreakLoopSkill(bool isSkipCurAnim = true) {
        if(m_curSkill != null) { m_curSkill.BreakLoop(isSkipCurAnim); }
    }

}
}
